package io.adrainty.bolee.framework.gateway.decorator;

import cn.hutool.core.text.StrPool;
import com.fasterxml.jackson.core.type.TypeReference;
import io.adrainty.bolee.framework.commons.basic.ResponseCode;
import io.adrainty.bolee.framework.commons.basic.ResultDTO;
import io.adrainty.bolee.framework.commons.exception.CoreException;
import io.adrainty.bolee.framework.commons.log.LogBusinessEntity;
import io.adrainty.bolee.framework.commons.utils.JsonUtil;
import io.adrainty.bolee.framework.commons.utils.RegionUtil;
import io.adrainty.bolee.framework.gateway.utils.RequestHelper;
import io.adrainty.bolee.framework.mq.basic.MQMessage;
import io.adrainty.bolee.framework.mq.basic.MQMessageType;
import io.adrainty.bolee.framework.mq.utils.RabbitMQUtil;
import jakarta.annotation.Nonnull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.reactivestreams.Publisher;
import org.springframework.cloud.stream.function.StreamBridge;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Optional;

/**
 * @author AdRainty
 * @version V1.0.0
 * @since 2025/7/27 下午11:07
 */

@Slf4j
public class CacheServerHttpResponseDecorator extends ServerHttpResponseDecorator {

    private final ServerWebExchange exchange;

    private final StreamBridge streamBridge;

    private final Long messageId;

    private final String sender;

    public CacheServerHttpResponseDecorator(
            StreamBridge streamBridge,
            Long messageId,
            String sender,
            ServerWebExchange exchange
    ) {
        super(exchange.getResponse());
        this.exchange = exchange;
        this.streamBridge = streamBridge;
        this.messageId = messageId;
        this.sender = sender;
    }

    @Nonnull
    @Override
    public Mono<Void> writeWith(@Nonnull Publisher<? extends DataBuffer> body) {
        if (body instanceof Flux<? extends DataBuffer> fluxBody) {
            return super.writeWith(fluxBody.buffer().map(dataBuffer -> {
                DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
                DataBuffer join = dataBufferFactory.join(dataBuffer);
                byte[] content = new byte[join.readableByteCount()];
                join.read(content);
                // 释放掉内存
                DataBufferUtils.release(join);
                String resultString = new String(content, StandardCharsets.UTF_8);
                //返回结果处理
                ResultDTO<Object> resultDTO = JsonUtil.fromJson(resultString, new TypeReference<>() {});
                // 处理下获取公钥之类的不是responseResult的消息
                if (resultDTO.getSuccess() == null && resultDTO.getData() == null) {
                    resultDTO = ResultDTO.builder()
                            .code(Integer.parseInt(ResponseCode.SUCCESS.getCode()))
                            .msg(ResponseCode.SUCCESS.getDesc())
                            .data(resultString)
                            .build();
                }

                // 服务访问记录-ResponseResult类型
                this.trace(exchange.getRequest(), resultDTO);
                // 返回响应数据
                byte[] uppedContent = resultString.getBytes();
                return getDelegate().bufferFactory().wrap(uppedContent);
            }));
        }
        return super.writeWith(body);
    }

    private void trace(ServerHttpRequest request, ResultDTO<Object> resultDTO) {
        try {
            String logJsonString = createLogJsonString(request, resultDTO);
            sendLogMQ(logJsonString);
        } catch (Exception e) {
            log.warn("sendLogMQ error, ignore: ", e);
        }

    }

    private String createLogJsonString(ServerHttpRequest request, ResultDTO<Object> resultDTO) {
        String requestIp = getRemoteIpAddr(request);
        RegionUtil.RegionData regionData = RegionUtil.getRegion(requestIp);
        String businessType = RequestHelper.getFirstHeader(request, "businessType");
        String deviceNumber = RequestHelper.getFirstHeader(request, "deviceNumber");

        String requestBody = null;
        if (request instanceof CacheServerHttpRequestDecorator decorator) {
            byte[] byteData = decorator.getBytes();
            if (byteData != null) requestBody = new String(byteData);
        }

        LogBusinessEntity logBusinessEntity = LogBusinessEntity.builder()
                .requestTid(request.getId())
                .host(request.getURI().getHost())
                .hostAddress(requestIp)
                .requestUri(request.getURI().getPath())
                .requestBody(requestBody)
                .requestMethod(request.getMethod().name())
                .responseBody(JsonUtil.toJson(resultDTO.getData()))
                .responseCode(String.valueOf(resultDTO.getCode()))
                .responseMsg(resultDTO.getMsg())
                .userId(String.valueOf(resultDTO.getOperatorId()))
                .username(resultDTO.getOperatorName())
                .businessType(businessType)
                .deviceNumber(deviceNumber)
                .region(regionData.toString())
                .build();
        return JsonUtil.toJson(logBusinessEntity);
    }

    private void sendLogMQ(String logJsonString) {
        MQMessage mqMessage = MQMessage.builder()
                .messageId(messageId)
                .content(logJsonString)
                .messageType(MQMessageType.LOG_MESSAGE.name())
                .createdTime(LocalDateTime.now())
                .sender(sender)
                .build();
        Message<MQMessage> message = MessageBuilder.withPayload(mqMessage).build();
        streamBridge.send(RabbitMQUtil.getProducerChannelName(MQMessageType.LOG_MESSAGE), message);
    }

    /**
     * 获取客户端ip地址
     * @return ip地址
     */
    private static String getRemoteIpAddr(ServerHttpRequest request) {
        String ip;
        String unknown = "unknown";
        try {
            ip = RequestHelper.getFirstHeader(request, "x-forwarded-for");
            if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
                ip = RequestHelper.getFirstHeader(request, "Proxy-Client-IP");
            }
            if (StringUtils.isEmpty(ip) || ip.isEmpty() || unknown.equalsIgnoreCase(ip)) {
                ip = RequestHelper.getFirstHeader(request, "WL-Proxy-Client-IP");
            }
            if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
                ip = RequestHelper.getFirstHeader(request, "HTTP_CLIENT_IP");
            }
            if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
                ip = RequestHelper.getFirstHeader(request, "HTTP_X_FORWARDED_FOR");
            }
            if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
                ip = Optional.ofNullable(request.getRemoteAddress())
                        .map(InetSocketAddress::getAddress)
                        .map(InetAddress::getHostAddress)
                        .orElse(StringUtils.EMPTY);
            }
        } catch (Exception e) {
            throw new CoreException(e);
        }

        if (StringUtils.isEmpty(ip) && ip.contains(StrPool.COMMA)) {
            ip = ip.substring(0, ip.indexOf(StrPool.COMMA));
        }

        return ip;
    }
    
}
